package com.zjl.SpringBoot.第06章_基本特性.E_单元测试;

import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.EmptyStackException;
import java.util.Stack;

/**
 * 详见 test 文件
 * JUnit5常用注解
 * JUnit5的注解与JUnit4的注解有所变化
 * https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
 * ● @Test :表示方法是测试方法。但是与JUnit4的@Test不同，他的职责非常单一不能声明任何属性，拓展的测试将会由Jupiter提供额外测试
 * ● @ParameterizedTest :表示方法是参数化测试，下方会有详细介绍
 * ● @RepeatedTest :表示方法可重复执行，下方会有详细介绍
 * ● @DisplayName :为测试类或者测试方法设置展示名称
 * ● @BeforeEach :表示在每个单元测试之前执行
 * ● @AfterEach :表示在每个单元测试之后执行
 * ● @BeforeAll :表示在所有单元测试之前执行
 * ● @AfterAll :表示在所有单元测试之后执行
 * ● @Tag :表示单元测试类别，类似于JUnit4中的@Categories
 * ● @Disabled :表示测试类或测试方法不执行，类似于JUnit4中的@Ignore
 * ● @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
 * ● @ExtendWith :为测试类或测试方法提供扩展类引用
 * ***************************************************************************************
 * 断言
 * 方法	                说明
 *  Assertions.assertEquals	        判断两个对象或两个原始类型是否相等
 *  Assertions.assertNotEquals	    判断两个对象或两个原始类型是否不相等
 *  Assertions.assertSame	        判断两个对象引用是否指向同一个对象
 *  Assertions.assertNotSame	    判断两个对象引用是否指向不同的对象
 *  Assertions.assertTrue	        判断给定的布尔值是否为 true
 *  Assertions.assertFalse	        判断给定的布尔值是否为 false
 *  Assertions.assertNull	        判断给定的对象引用是否为 null
 *  Assertions.assertNotNull	    判断给定的对象引用是否不为 null
 *  Assertions.assertArrayEquals	数组断言
 *  Assertions.assertAll	        组合断言
 *  Assertions.assertThrows	        异常断言
 *  Assertions.assertTimeout	    超时断言
 *  Assertions.fail	                快速失败
 * 嵌套测试
 * JUnit 5 可以通过 Java 中的内部类和 @Nested 注解实现嵌套测试，从而可以更好的把相关的测试方法组织在一起
 * 在内部类中可以使用@BeforeEach 和 @AfterEach 注解，而且嵌套的层次没有限制
 * ***************************************************************************************
 * 参数化测试
 * 参数化测试是JUnit5很重要的一个新特性，它使得用不同的参数多次运行测试成为了可能，也为单元测试带来许多便利
 * 利用**@ValueSource**等注解，将可以使用不同的参数进行多次单元测试，而不需要每新增一个参数就新增一个单元测试，省去了很多冗余代码
 *      @ValueSource：为参数化测试指定入参来源，支持八大基础类以及String类型，Class类型
 *      @NullSource：表示为参数化测试提供一个null的入参
 *      @EnumSource：表示为参数化测试提供一个枚举入参
 *      @CsvFileSource：表示读取指定CSV文件内容作为参数化测试入参
 *      @MethodSource：表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)参数化测试
 *
 *
 */
@SpringBootTest("application.properties")//具备  SpringBoot 所有组件的东西
@SpringBootApplication( exclude = {DataSourceAutoConfiguration.class})
class E_单元测试 {

    @Test
    void contextLoads() {
//        Assertions.assertEquals(1,1);
    }

    @ParameterizedTest
    @ValueSource(strings = {"one","two"})//会执行 两遍
    @DisplayName("参数化测试1")
    void 参数化测试1(String s) {
        System.out.println(s);
        Assertions.assertTrue(StringUtils.isNotBlank(s));
    }
    @ParameterizedTest
    @MethodSource("getString")//根据方法的返回值 根据数量 每一个执行
    @DisplayName("参数化测试2")
    void 参数化测试2(String s) {
        System.out.println(s);
        Assertions.assertTrue(StringUtils.isNotBlank(s));
    }
    static String[] getString(){
        return new String[]{"xx1","xx2"};
    }


    @DisplayName("A stack")
    class TestingAStackDemo {
        Stack<Object> stack;
        @Test
        @DisplayName("is instantiated with new Stack()")
        void isInstantiatedWithNew() {
            new Stack<>();
        }
        @Nested
        @DisplayName("when new")
        class WhenNew {
            @BeforeEach
            void createNewStack() {
                stack = new Stack<>();
            }
            @Test
            @DisplayName("is empty")
            void isEmpty() {
                Assertions.assertTrue(stack.isEmpty());
            }
            @Test
            @DisplayName("throws EmptyStackException when popped")
            void throwsExceptionWhenPopped() {
                Assertions.assertThrows(EmptyStackException.class, stack::pop);
            }
            @Test
            @DisplayName("throws EmptyStackException when peeked")
            void throwsExceptionWhenPeeked() {
                Assertions.assertThrows(EmptyStackException.class, stack::peek);
            }
            @Nested
            @DisplayName("after pushing an element")
            class AfterPushing {
                String anElement = "an element";
                @BeforeEach
                void pushAnElement() {
                    stack.push(anElement);
                }
                @Test
                @DisplayName("it is no longer empty")
                void isNotEmpty() {
                    Assertions.assertFalse(stack.isEmpty());
                }
                @Test
                @DisplayName("returns the element when popped and is empty")
                void returnElementWhenPopped() {
                    Assertions.assertEquals(anElement, stack.pop());
                    Assertions.assertTrue(stack.isEmpty());
                }
                @Test
                @DisplayName("returns the element when peeked but remains not empty")
                void returnElementWhenPeeked() {
                    Assertions.assertEquals(anElement, stack.peek());
                    Assertions.assertFalse(stack.isEmpty());
                }
            }
        }
    }
}



